
#if defined(WIN32) || defined(WIN64)
#pragma unmanaged
#endif

#include <Liberty3D/Engine/Path.h>
#include <Liberty3D/Engine/Conversion.h>

using namespace Liberty::Engine;

#ifdef WIN32
void Path::winifyPath(std::string& s) {
    std::string::iterator it, end(s.end());

    for (it = s.begin(); it != end; ++it) {
        if (*it == '/') {
            *it = '\\';
        }
    }
}

void Path::winifyPath(std::basic_string<wchar_t>& s) {
    std::basic_string<wchar_t>::iterator it, end(s.end());

    for (it = s.begin(); it != end; ++it) {
        if (*it == L'/') {
            *it = L'\\';
        }
    }
}
#endif

void Path::unixifyPath(std::string& s) {
    std::string::iterator it, end(s.end());

    for (it = s.begin(); it != end; ++it) {
        if (*it == '\\') {
            *it = '/';
        }
    }
}

void Path::removeSuffixSlash(std::string& s) {
    size_t f = s.length();
    
    if (f > 0) {
        char lc = s[f - 1];
        
        if ((lc == '/') ||
            (lc == '\\')) {
            s.erase(f - 1, 1);
        }
    }
}

void Path::ensureSuffixSlash(std::string& s) {
    size_t f = s.length();
    
    if (f > 0) {
        char lc = s[f - 1];
        
        if ((lc != '/') &&
            (lc != '\\')) {
            s.insert(s.end(), '/');
        }
    }
}

std::string Path::append(const std::string& p1, const std::string& p2) {
    std::string p(p1);
    
    ensureSuffixSlash(p);
    p += p2;
    return p;
}

std::string Path::split(const std::string& p1, std::string* p2) {
    const char* p = p1.c_str();
    const char* r = strrchr(p, '/');
    
    if (r == nullptr) {
        if (p2) {
            *p2 = p;
        }
        
        return std::string();
    }
    
    std::string s;
    
    s.insert(s.begin(), p, r + 1);
    
    if (p2) {
        *p2 = r + 1;
    }
    
    return s;
}

std::string Path::splitExtension(const std::string& p1, std::string* p2) {
    const char* p = p1.c_str();
    const char* r = strrchr(p, '.');
    const char* t = strrchr(p, '/');

    if ((r == nullptr) || (r < t)) {
        if (p2) {
            *p2 = std::string();
        }
        
        return p;
    }

    std::string s;
    
    s.insert(s.begin(), p, r);
    
    if (p2) {
        *p2 = r;
    }

    return s;
}

std::vector<std::string> Path::splitParts(const std::string& p1, char c) {
    const char* p     = p1.c_str();
    size_t      count = 0;

    while (*p == c) {
        p++;
    }

    const char* pp  = p;
    const char* ppp = p;

    while ((pp = strchr(pp, c))) {
        while (pp[1] == c) {
            pp++;
        }

        count++, pp++;
        ppp = pp;
    }

    if (*ppp) {
        count++;
    }

    std::vector<std::string> parts(count);

    count = 0;
    pp    = p;
    ppp   = p;

    while ((pp = strchr(pp, c))) {
        parts[count] = std::string(ppp, pp);

        while (pp[1] == c) {
            pp++;
        }

        count++, pp++;
        ppp = pp;
    }

    if (*ppp) {
        parts[count] = ppp;
    }

    return parts;
}

std::string Path::normalize(const std::string& name) {
    std::vector<std::string> parts(splitParts(name));

    size_t index, count(parts.size());

    for (index = 0; index < count; index++) {
        const std::string& p(parts[index]);

        if (p == ".") {
            parts.erase(parts.begin() + index);
            index--, count--;
        }
        else if (p == "..") {
            if (index > 0) {
                parts.erase(parts.begin() + (index - 1), parts.begin() + (index + 1));
                index -= 2, count -= 2;
            }
        }
    }

    std::string rel;
    rel.reserve(name.length());

    for (index = 0; index < count; index++) {
        if (index) {
            rel += "/";
        }

        rel += parts[index];
    }

    return rel;
}

std::string Path::relativeTo(const std::string& name, const std::string& otherFile) {
    std::string folder (split(name));
    std::string newPath(append(folder, otherFile));

    return normalize(newPath);
}
